﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Xml;

namespace Cashier_Upgrade
{
    public partial class UpgradeForm : Form
    {
        private String httpUrl;
        private String localPath;
        List<UpgradeInfo> gradeInfoList = null;
        private String mainProgramName;
        private String configXmlPath; //配置文件路径
        private String sqlVersion; //远程SQL文件版本

        /// <summary>
        /// 定义委托
        /// </summary>
        /// <param name="fileCount"></param>
        private delegate void UpdateUI(int fileCount);

        /// <summary>
        /// 更新控件状态
        /// </summary>
        /// <param name="gradeInfoCount"></param>
        private delegate void UpdateControlState(int gradeInfoCount, bool isExit);

        /// <summary>
        /// 构造函数
        /// </summary>
        public UpgradeForm()
        {
            InitializeComponent();
            label_msg.Text = "检测是否有可升级的程序版本...";
            button_upgrade.Enabled = false;
            Thread thread = new Thread(new ThreadStart(CheckNewVersion));
            thread.Start();
        }

        /// <summary>
        /// 检测新版本
        /// </summary>
        public void CheckNewVersion()
        {
            bool isExit = true;
            httpUrl = "http://www.daysmei.com";
            localPath = System.Windows.Forms.Application.StartupPath;

            //判断是否与程序集在同一目录下，否则不执行升级程序
            DirectoryInfo dir = new DirectoryInfo(this.localPath);
            FileInfo[] files = dir.GetFiles();
            if (files != null && files.Length > 0)
            {
                foreach (FileInfo file in files)
                {
                    if (file.Extension.ToString().Equals(".exe"))
                    {
                        System.Diagnostics.FileVersionInfo info = System.Diagnostics.FileVersionInfo.GetVersionInfo(file.FullName);
                        String name = info.ProductName;
                        if (name.ToLower().Equals("cashier"))
                        {
                            isExit = false;
                            mainProgramName = file.Name;
                            break;
                        }
                    }
                }
            }

            int gradeInfoCount = 0;
            if (isExit)
            {
                BeginInvoke(new UpdateControlState(ChangeControlState), new object[] { gradeInfoCount, isExit });
                return;
            }
            gradeInfoList = this.GetVersionInfo();
            if (gradeInfoList != null)
                gradeInfoCount = gradeInfoList.Count;
            else
                gradeInfoCount = -1;

            if (this.IsHandleCreated)
            {
                BeginInvoke(new UpdateControlState(ChangeControlState), new object[] { gradeInfoCount, isExit });
            }
        }

        /// <summary>
        /// 更新按钮状态
        /// </summary>
        /// <param name="gradeInfoCount"></param>
        /// <param name="isExit"></param>
        private void ChangeControlState(int gradeInfoCount, bool isExit)
        {
            button_upgrade.Enabled = true;
            if (gradeInfoCount > 0)
            {
                label_msg.Text = "检测到有新的版本可更新，建议立即更新";
            }
            else if (gradeInfoCount < 0)
            {
                label_msg.Text = "网络连接异常，请确定联网并重试!";
                button_upgrade.Text = "退出";
            }
            else
            {
                label_msg.Text = "当前已是最新版本";
                button_upgrade.Text = "退出";
            }

            if (isExit)
            {
                MessageBox.Show("请将升级程序复制到收银软件目录中再运行！");
                this.Close();
            }
        }

        /// <summary>
        /// 获取需要升级的程序信息
        /// </summary>
        /// <returns></returns>
        public List<UpgradeInfo> GetVersionInfo()
        {
            List<UpgradeInfo> list = new List<UpgradeInfo>();
            String url = this.httpUrl + "/client/upgrade.jsp?method=versioninfo&clientType=single";
            try
            {
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
                HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
                Stream stream = webResponse.GetResponseStream();
                StreamReader sr = new StreamReader(stream, System.Text.Encoding.GetEncoding("utf-8"));
                String info = sr.ReadToEnd();

                if (!String.IsNullOrEmpty(info))
                {
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(info);
                    XmlElement root = doc.DocumentElement;
                    XmlNodeList nodeList = root.ChildNodes;
                    foreach (XmlNode lopNode in nodeList)
                    {
                        XmlNodeList childNodeList = lopNode.ChildNodes;
                        String name = String.Empty;
                        String path = String.Empty;
                        String version = String.Empty;
                        long size = 0L;
                        String type = String.Empty;
                        int versionCode = 0;
                        foreach (XmlNode lopChildNode in childNodeList)
                        {
                            String nodeName = lopChildNode.Name;
                            if (nodeName.Equals("name"))
                            {
                                name = lopChildNode.InnerText;
                            }
                            else if (nodeName.Equals("path"))
                            {
                                path = lopChildNode.InnerText;
                            }
                            else if (nodeName.Equals("version"))
                            {
                                version = lopChildNode.InnerText;
                            }
                            else if (nodeName.Equals("size"))
                            {
                                size = long.Parse(lopChildNode.InnerText.Trim());
                            }
                            else if (nodeName.Equals("type"))
                            {
                                type = lopChildNode.InnerText;
                            }
                            else if (nodeName.Equals("versionCode"))
                            {
                                versionCode = Int32.Parse(lopChildNode.InnerText.Trim());
                            }
                        }

                        if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(path) && !String.IsNullOrEmpty(version))
                        {
                            int versionInt = 0;
                            int localVersionInt = 0;
                            FileInfo fileInfo = new FileInfo(localPath + @"\" + name);
                            if (type.Equals("exe") || type.Equals("dll"))
                            {
                                versionInt = Int32.Parse(version.Replace(".", ""));
                                if (fileInfo.Exists)
                                {
                                    String localVersion = GetProgramVersion(name);
                                    localVersionInt = Int32.Parse(localVersion.Replace(".", ""));
                                }
                                else
                                {
                                    localVersionInt = 0;
                                }
                            }
                            if (type.Equals("txt") || type.Equals("sql"))
                            {
                                versionInt = Int32.Parse(version.Replace(".", ""));
                                doc.Load(localPath + @"\config.xml");
                                root = doc.DocumentElement;
                                String versionNode = "version";
                                if (type.Equals("sql"))
                                {
                                    versionNode = "sqlversion";
                                    this.sqlVersion = version;
                                }
                                String localVersion = String.Empty;
                                XmlNode xmlNode = root.SelectSingleNode("/config/setting/" + versionNode);
                                if (xmlNode != null)
                                {
                                    localVersion = xmlNode.InnerText;
                                }
                                if (String.IsNullOrEmpty(localVersion))
                                {
                                    localVersion = "1.0.0.0";
                                }
                                localVersionInt = Int32.Parse(localVersion.Replace(".", ""));
                            }
                            if (versionInt > localVersionInt)
                            {
                                UpgradeInfo upgradeInfo = new UpgradeInfo();
                                upgradeInfo.FileName = name;
                                upgradeInfo.FilePath = path;
                                upgradeInfo.Version = version;
                                upgradeInfo.Size = size;
                                upgradeInfo.Type = type;
                                upgradeInfo.VersionCode = versionCode;
                                list.Add(upgradeInfo);
                            }
                            fileInfo = null;
                        }
                    }
                }
            }
            catch (WebException webex)
            {
                list = null;
                MessageBox.Show("网络连接超时！\r\n" + webex.Message);
            }
            catch (Exception ex)
            {
                list = null;
                MessageBox.Show("升级出错，请稍后再试！\r\n" + ex.Message);
            }
            return list;
        }

        /// <summary>
        /// 获取程序集版本号
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public String GetProgramVersion(String programName)
        {
            System.Diagnostics.FileVersionInfo info = System.Diagnostics.FileVersionInfo.GetVersionInfo(this.localPath + @"\" + programName);
            string version = info.FileVersion;
            return version;
        }

        /// <summary>
        /// 下载服务器程序文件
        /// </summary>
        /// <param name="obj"></param>
        public void DownloadAssembly(Object obj)
        {
            Stream stream = null;
            FileStream fileStream = null;
            try
            {
                DownloadParam downloadParam = (DownloadParam)obj;
                String downloadUrl = downloadParam.DownloadUrl;
                String savePath = downloadParam.SavePath;

                FileInfo fileInfo = new FileInfo(savePath);
                if (fileInfo.Exists)
                {
                    fileInfo.Delete();
                }
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(downloadUrl);
                HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
                stream = webResponse.GetResponseStream();
                fileStream = new FileStream(savePath, FileMode.Create);
                byte[] bt = new byte[1024];
                int length = 0;
                while ((length = stream.Read(bt, 0, bt.Length)) > 0)
                {
                    fileStream.Write(bt, 0, length);
                }
                fileStream.Flush();
                fileStream.Close();
                stream.Close();
                BeginInvoke(new UpdateUI(ProcessBarGrow), new object[] { 1 });
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                }
                if (stream != null)
                {
                    stream.Close();
                }
            }
        }

        /// <summary>
        /// 判断收银软件是否正在运行
        /// </summary>
        /// <returns></returns>
        private bool ExistProcess()
        {
            bool b = false;
            Process[] processArr = Process.GetProcessesByName(mainProgramName.Substring(0, mainProgramName.IndexOf(".")));
            if (processArr != null && processArr.Length > 0)
            {
                b = true;
            }
            return b;
        }

        /// <summary>
        /// 移动下载文件替换旧程序，如果有SQL文件，则返回sql文件路径
        /// </summary>
        /// <param name="dir"></param>
        private String MoveDownloadFile(DirectoryInfo dir)
        {
            String sqlFile = String.Empty;
            FileInfo[] fileInfos = dir.GetFiles();
            if (fileInfos != null && fileInfos.Length > 0)
            {
                try
                {
                    FileInfo oldFile = null;
                    foreach (FileInfo lopFileInfo in fileInfos)
                    {
                        if (lopFileInfo.Extension.ToLower().Equals(".sql"))
                        {
                            sqlFile = lopFileInfo.FullName;
                        }
                        else if (lopFileInfo.Name.ToLower().Equals("config.txt"))
                        {
                            this.configXmlPath = lopFileInfo.FullName;
                        }
                        else
                        {
                            oldFile = new FileInfo(this.localPath + @"\" + lopFileInfo.Name);
                            if (oldFile.Exists)
                            {
                                oldFile.Delete();
                            }
                            lopFileInfo.MoveTo(this.localPath + @"\" + lopFileInfo.Name);
                        }
                    }
                }
                catch (IOException ex)
                {
                    throw ex;
                }
            }
            return sqlFile;
        }

        /// <summary>
        /// 执行SQL文件
        /// </summary>
        /// <param name="sqlfilePath"></param>
        private List<String> ReadSqlFromText(String sqlfilePath)
        {
            List<String> sqlList = null;
            FileInfo sqlFile = new FileInfo(sqlfilePath);
            if (sqlFile.Exists)
            {
                Stream stream = null;
                StreamReader sr = null;
                try
                {
                    sqlList = new List<string>();
                    stream = new FileStream(sqlFile.FullName, FileMode.Open);
                    sr = new StreamReader(stream, Encoding.UTF8);
                    StringBuilder sb = new StringBuilder();
                    String line = String.Empty;
                    while (sr.Peek() > -1)
                    {
                        line += sr.ReadLine();
                        if (!String.IsNullOrEmpty(line) && !line.StartsWith("#") && !line.StartsWith("/*"))
                        {
                            if (line.EndsWith(";"))
                            {
                                //line = line.Substring(0, line.Length - 1);
                                sqlList.Add(line);
                                Console.WriteLine(line);
                                line = String.Empty;
                            }
                        }
                        else
                        {
                            line = String.Empty;
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    if (sr != null)
                        sr.Close();
                    if (stream != null)
                        stream.Close();
                }
            }
            return sqlList;
        }

        /// <summary>
        /// 执行XML配置文件
        /// </summary>
        /// <param name="xmlPath"></param>
        private void SaveConfigXml(String configPath)
        {
            FileInfo fileInfo = new FileInfo(configPath);
            if (fileInfo.Exists)
            {
                Stream stream = null;
                StreamReader sr = null;
                try
                {
                    stream = new FileStream(configPath, FileMode.Open);
                    sr = new StreamReader(stream, Encoding.UTF8);
                    List<String> list = new List<string>();
                    XmlDocument dom = new XmlDocument();
                    dom.Load(this.localPath + @"\config.xml");
                    while (sr.Peek() > -1)
                    {
                        String line = sr.ReadLine();
                        if (!String.IsNullOrEmpty(line))
                        {
                            String[] arrNode = line.Split(new char[] { ',' });
                            String elementName = arrNode[0];
                            String[] childArr = arrNode[1].Split(new char[] { '|' });
                            String childName = childArr[0];
                            String childValue = childArr.Length > 1 ? childArr[1] : String.Empty;
                            XmlNode parentNode = dom.SelectSingleNode(elementName);
                            if (parentNode != null)
                            {
                                XmlNode childNode = dom.SelectSingleNode(elementName + "/" + childName);
                                if (childNode == null)
                                {
                                    XmlElement element = dom.CreateElement(childName);
                                    element.InnerText = childValue;
                                    parentNode.AppendChild(element);
                                }
                                else
                                {
                                    childNode.InnerText = childValue;
                                }
                            }
                        }
                    }
                    dom.Save(this.localPath + @"\config.xml");
                }
                catch
                { }
                finally
                {
                    if (sr != null)
                    {
                        sr.Close();
                    }
                    if (stream != null)
                    {
                        stream.Close();
                    }
                }
            }
        }

        /// <summary>
        /// 更新事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_upgrade_Click(object sender, EventArgs e)
        {
            try
            {
                if (gradeInfoList != null && gradeInfoList.Count > 0)
                {
                    if (this.ExistProcess())
                    {
                        MessageBox.Show("升级程序之前需要退出收银软件，请手动关闭收银软件!");
                        return;
                    }

                    label_msg.Text = "正在下载升级程序，此过程可能需要持续几分钟，请稍等...";
                    button_upgrade.Enabled = false;
                    DirectoryInfo dir = new DirectoryInfo(this.localPath + @"\upgrade");
                    if (!dir.Exists)
                        dir.Create();

                    //设置进度条最小值和最大值
                    progressBar1.Minimum = 0;
                    progressBar1.Maximum = gradeInfoList.Count + 2;

                    foreach (UpgradeInfo upgradeInfo in gradeInfoList)
                    {
                        ParameterizedThreadStart paramStart = new ParameterizedThreadStart(DownloadAssembly);
                        Thread myThread = new Thread(paramStart);
                        DownloadParam downloadParam = new DownloadParam();
                        downloadParam.DownloadUrl = httpUrl + upgradeInfo.FilePath;
                        downloadParam.SavePath = dir.FullName + @"\" + upgradeInfo.FileName;
                        myThread.Start(downloadParam);
                    }
                }
                else
                {
                    this.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 刷新进度条进度
        /// </summary>
        /// <param name="fileCount"></param>
        private void ProcessBarGrow(int fileCount)
        {
            progressBar1.Value += fileCount;
            if (progressBar1.Value == gradeInfoList.Count)
            {
                try
                {
                    DirectoryInfo dir = new DirectoryInfo(this.localPath + @"\upgrade");
                    String sqlFilePath = this.MoveDownloadFile(dir);
                    if (!String.IsNullOrEmpty(this.configXmlPath))
                    {
                        this.SaveConfigXml(this.configXmlPath);
                    }
                    if (!String.IsNullOrEmpty(sqlFilePath))
                    {
                        List<String> sqlList = this.ReadSqlFromText(sqlFilePath);
                        int count = new SQLiteUpgrade().DoWhileBatchExecuteSQL(sqlList);
                        if (count == sqlList.Count)
                        {
                            XmlDocument dom = new XmlDocument();
                            dom.Load(this.localPath + @"\config.xml");
                            XmlElement root = dom.DocumentElement;
                            XmlNode node = root.SelectSingleNode("/config/setting/sqlversion");
                            node.InnerText = sqlVersion;
                            dom.Save(this.localPath + @"\config.xml");
                        }
                    }
                    progressBar1.Value += 1;
                    if (dir.Exists)
                    {
                        dir.Delete(true);
                    }
                    progressBar1.Value += 1;
                }
                catch(Exception ex) 
                {
                    MessageBox.Show("升级出错：" + ex.Message);
                    button_upgrade.Text = "退出";
                    button_upgrade.Enabled = true;
                }
            }
            if (progressBar1.Value == gradeInfoList.Count + 2)
            {
                gradeInfoList = null;
                MessageBox.Show("更新完毕！");
                label_msg.Text = "已经升级为最新版本";
                button_upgrade.Text = "退出";
                button_upgrade.Enabled = true;
            }
        }
    }

    public class DownloadParam
    {
        private String downloadUrl;

        public String DownloadUrl
        {
            get { return downloadUrl; }
            set { downloadUrl = value; }
        }

        private String savePath;

        public String SavePath
        {
            get { return savePath; }
            set { savePath = value; }
        }
    }
}
